package mware_lib;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.Socket;
import java.util.HashMap;

public class SkeletonRequestExecuter implements Runnable{
	OutputStream out; 
	BufferedReader in; 
	/**
	 * contains all local bound objects
	 */
	HashMap<String, Object> obj_map;
	Socket mySock;
	InputStreamReader in_low;
	/**
	 * hostname of name service
	 */
	String ns_hostname;
	/**
	 * port of name service
	 */
	int ns_port;
	
	public SkeletonRequestExecuter(Socket mySock, HashMap<String, Object> obj_map, String ns_hostname, int ns_port) {
		this.mySock = mySock;
		this.obj_map = obj_map;
		this.ns_hostname = ns_hostname;
		this.ns_port = ns_port;
	}

	@Override
	public void run() {
		String incoming = null;
		
		try {
			out = mySock.getOutputStream();
			in_low = new InputStreamReader(mySock.getInputStream());
			in = new BufferedReader(in_low);
			out = mySock.getOutputStream();
			
			incoming = in.readLine();
	        System.out.println("Skeleton: incoming- "+incoming);
	        
	      
	        String[] splitted;
	        if(incoming != null){
                splitted = incoming.split(":");
	        }else{
                splitted = new String[0];
                System.out.println("Skeleton: get only null!");
	        }
	        
	        //delegate by first command
	        if(splitted.length > 1){
	            if(splitted[0].equals("MethodRequest") ){
	            	methodRequest(splitted);
	            }else if(splitted[0].equals("unbind") ){
	            	removeName(splitted[1]);
	            }
	        }else{
	        	out.write("RemoteException:BadCommand".getBytes());
	        	out.flush();
	        }
	        
	        out.flush();
	        mySock.close();
	        in.close();
	        out.close();
        
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}
	
	/**
	 * 
	 * @param splitted incomming command from stubs
	 * @throws IOException
	 */
	private void methodRequest(String[] splitted) throws IOException {
		System.out.println("Skeleton: MethodRequest");
		Object origin;
		Object returnV = null;
		
		try {
			if(obj_map.containsKey(splitted[1])){
		        //look up for all necessary information to perfome the method
				origin = obj_map.get(splitted[1]);      
		        Class<?>[] pTypes = new Class[Integer.parseInt(splitted[3])];
		        Object[] params = new Object[pTypes.length]; 
		        
		        //collect param types
		        for (int i = 0; i < pTypes.length; i++) {
	                
		        	pTypes[i] = Class.forName(splitted[4+i]);
					
	                System.out.println("NS: params " + i+":"+pTypes[i]);
		        }
		        
		        //collect param values of method
		        for (int i = 0; i < params.length; i++) {
		        	
	                if(pTypes[i].equals(Double.class)){                                                                     
	                	params[i] = pTypes[i].getConstructor(double.class).newInstance( Double.parseDouble(splitted[4+i+params.length]));
	                	pTypes[i] = double.class;
	                }else if(pTypes[i].equals(String.class)){                                                                	
	                	params[i] = pTypes[i].getConstructor(pTypes[i]).newInstance( splitted[4+i+params.length] );
	                }else{
	                	System.err.println("unbekannter ParameterTyp");
	                }			                
		        }
		        
		        //now get the needed methode
		        Method m = origin.getClass().getMethod(splitted[2], pTypes);
		        
		        try{
		        	//with or without return value
	                if(m.getReturnType() == void.class){
	                	
	                    m.invoke(origin, params);
	                    System.out.println("Skeleton: no return value");
	                    out.write( ("NoReturnValue\n").getBytes() );
	                }else{
	                	
	            		for (Object elem : params) {
							System.out.println("param: " + elem);
						}
	                    returnV = m.invoke(origin, params);
	                    System.out.println("Skeleton: return value - " +returnV);
	                    out.write( ("ReturnValue:"+returnV+"\n").getBytes() );
	                    out.flush();
	                }
		        }catch(InvocationTargetException  e){
		        	System.out.println("InvocationTargetException");
		        	out.write( ("InvocationTargetException:"+e.getCause().getClass().getName() + ":" + e.getTargetException().getMessage() + "\n").getBytes() );
		        }    
			}
			
//			catch all possible exceptions and send a message back to the stub
		}catch (ClassNotFoundException e) {
			e.printStackTrace();
			System.err.println("Exception handled of a method-comand");
			out.write("RemoteException:Class was not found on Remote Host".getBytes());
		}catch (IOException e) {
			e.printStackTrace();
		}catch (IllegalArgumentException e) {
			e.printStackTrace();
			System.err.println("Exception handled of a method-comand");
			out.write("RemoteException:IllegalArgumentException on Remote Host".getBytes());
		}catch (IllegalAccessException e) {
			e.printStackTrace();
			System.err.println("Exception handled of a method-comand");
			out.write("RemoteException:IllegalAccessException on Remote Host".getBytes());
		} catch (SecurityException e) {
			e.printStackTrace();
			System.err.println("Exception handled of a method-comand");
			out.write("RemoteException:SecurityException on Remote Host".getBytes());
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
			System.err.println("Exception handled of a method-comand");
			out.write("RemoteException:Method was not found on Remote Host".getBytes());
		} catch (InstantiationException e) {
			e.printStackTrace();
			System.err.println("Exception handled of a method-comand");
			out.write("RemoteException:InstantiationException on Remote Host".getBytes());
		} catch (InvocationTargetException e) {
			e.printStackTrace();
			System.err.println("Exception handled of a method-comand");
			out.write("RemoteException:InvocationTargetException on Remote Host".getBytes());
		} catch(ArrayIndexOutOfBoundsException e){
			System.err.println("Exception handled of a method-comand");
			System.err.println("unvollst�ndige oder fehlerhafte Anfrage");
			out.write("RemoteException:Bad Command".getBytes());
		} catch(Exception e){
			e.printStackTrace();
			System.err.println("Exception handled of a method-comand");
			out.write("RemoteException:Exception".getBytes());
		}
	}

	
	public void removeName(String name){
    	synchronized (obj_map) {
    		obj_map.remove(name);
		}       
    	StubSkeletonCom.sendIt("unbind:"+name+"\n", false, ns_hostname, ns_port);
    }


}
